package Midium;

import java.util.Arrays;

//16. 最接近的三数之和
//思路:a、b、c分别对应三个数，首先固定a，然后b和c分别是已排序数组的两头。dis为三数之和与target的距离
//如果j往右，dis变大了，说明三个数的和应该越小才越接近，因此b不能往右移，要让c往左移
//如果k往左，dis变大了，说明三个数的和应该越大才越接近，因此c不能往右移，要让b往左移
//如果j不能往右，且k不能往左，则循环结束——找到了对于此a来说的最优b和c
//注意：最后再用本次的最优解与全局最优解进行比较！

//此算法的缺点：容易在有序数组的中间部分（负数与正数变化的那一段子数组）出现问题，因为dis使用绝对值的形式进行比较的，
// 在这一段子数组时有可能出现j往右或者k往左都使得dis变大从而终止本次对a的循环，错过了寻找最优解的情况

//补充:优化了同时减小的情况，通过了倒数第四个测试用例，但是倒数第三个测试用例又运行结果错误了。
//由于倒数第三个测试用例太长了，用debug的方式去寻找哪里出问题是在是太困难了，故放弃！
public class Solution16 {
    public static int threeSumClosest(int[] nums, int target) {
        if (nums.length == 3) {
            return nums[0] + nums[1] + nums[2];
        }
        Arrays.sort(nums);
        int f_a = nums[0], f_b = nums[1], f_c = nums[nums.length - 1];
        int minDis = Math.abs(target - nums[0] - nums[1] - nums[nums.length - 1]);
        for (int i = 0; i < nums.length - 2; i++) {
            int a = nums[i],b=0,c=0,dis=0;//初始化b,c,dis
            for (int j = i + 1, k = nums.length - 1; j < k; ) {
                b = nums[j];
                c = nums[k];
                dis = Math.abs(target - a - b - c);
                if (dis == 0) {
                    return target;
                }
                if (Math.abs(target - a - c - nums[j + 1]) > dis && Math.abs(target - a - b - nums[k - 1]) <= dis) {
                    k--;
                } else if (Math.abs(target - a - b - nums[k - 1]) > dis && Math.abs(target - a - c - nums[j + 1]) <= dis) {
                    j++;
                } else if(Math.abs(target - a - c - nums[j + 1]) <= dis && Math.abs(target - a - b - nums[k - 1]) <= dis){
                    // j往左或者k往右都可以的情况，选择二者中最优
                    if(Math.abs(target - a - c - nums[j + 1]) > Math.abs(target - a - b - nums[k - 1]))
                        k--;
                    else
                        j++;
                } else if (Math.abs(target - a - nums[k-1] - nums[j + 1]) <= dis){
                    // 优化：可能j往右且k往左的同时，能使得dis变得更小
                    j++;k--;
                } else break;
            }
            if (dis < minDis) {
                minDis = dis;
                f_a = a;
                f_b = b;
                f_c = c;
            }
        }
        return f_a + f_b + f_c;
    }

    public static void main(String[] args) {
        //test
        System.out.println(threeSumClosest(new int[]{1, 1, -1, -1, 3}, -1));
        System.out.println(threeSumClosest(new int[]{-1, 2, 1, -4}, 1));
        System.out.println(threeSumClosest(new int[]{1, 1, 1, 0}, 100));
        System.out.println(threeSumClosest(new int[]{1, -3, 3, 5, 4, 1}, 1));
        System.out.println(threeSumClosest(new int[]{-55,-24,-18,-11,-7,-3,4,5,6,9,11,23,33}, 0));//倒数第四个测试用例
        //预期结果应该是-58，我的运行结果是-57
        System.out.println(threeSumClosest(new int[]{13,2,0,-14,-20,19,8,-5,-13,-3,20,15,20,5,13,14,-17,-7,12,-6,0,20,-19,-1,-15,-2,8,-2,-9,13,0,-3,-18,-9,-9,-19,17,-14,-19,-4,-16,2,0,9,5,-7,-4,20,18,9,0,12,-1,10,-17,-11,16,-13,-14,-3,0,2,-18,2,8,20,-15,3,-13,-12,-2,-19,11,11,-10,1,1,-10,-2,12,0,17,-19,-7,8,-19,-17,5,-5,-10,8,0,-12,4,19,2,0,12,14,-9,15,7,0,-16,-5,16,-12,0,2,-16,14,18,12,13,5,0,5,6}, -59));//倒数第三个测试用例


    }
}
